/*** Eclipse Class Decompiler plugin, copyright (c) 2012 Chao Chen (cnfree2000@hotmail.com) ***/
package org.hibernate.cfg;


import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.persistence.Basic;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.PrimaryKeyJoinColumns;
import javax.persistence.SecondaryTable;
import javax.persistence.SecondaryTables;
import javax.persistence.SequenceGenerator;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.SqlResultSetMappings;
import javax.persistence.TableGenerator;
import javax.persistence.Transient;
import javax.persistence.Version;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode;
import org.hibernate.MappingException;
import org.hibernate.annotations.AccessType;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.Check;
import org.hibernate.annotations.CollectionId;
import org.hibernate.annotations.CollectionOfElements;
import org.hibernate.annotations.Columns;
import org.hibernate.annotations.DiscriminatorFormula;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.FilterDefs;
import org.hibernate.annotations.Filters;
import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.Formula;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.GenericGenerators;
import org.hibernate.annotations.Index;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.MapKeyManyToMany;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.ParamDef;
import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.Parent;
import org.hibernate.annotations.Proxy;
import org.hibernate.annotations.Sort;
import org.hibernate.annotations.Tables;
import org.hibernate.annotations.Target;
import org.hibernate.annotations.Tuplizer;
import org.hibernate.annotations.Tuplizers;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
import org.hibernate.annotations.Where;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XPackage;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.cfg.annotations.CollectionBinder;
import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.cfg.annotations.Nullability;
import org.hibernate.cfg.annotations.PropertyBinder;
import org.hibernate.cfg.annotations.QueryBinder;
import org.hibernate.cfg.annotations.SimpleValueBinder;
import org.hibernate.cfg.annotations.TableBinder;
import org.hibernate.engine.FilterDefinition;
import org.hibernate.id.MultipleHiLoPerTableGenerator;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.DependantValue;
import org.hibernate.mapping.IdGenerator;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.JoinedSubclass;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.SingleTableSubclass;
import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.UnionSubclass;
import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
import org.hibernate.persister.entity.SingleTableEntityPersister;
import org.hibernate.persister.entity.UnionSubclassEntityPersister;
import org.hibernate.type.TypeFactory;
import org.hibernate.util.StringHelper;

/**
 * 通过jad反编译后的代码
 * add by wanghuawen for @Comment 注解
 * version 3.3.1
 */
@SuppressWarnings({"rawtypes","unchecked"})
public final class AnnotationBinder
{
    private static final Log log;

    public static void bindDefaults(ExtendedMappings mappings)
    {
        
        Map defaults = mappings.getReflectionManager().getDefaults();

        List<SequenceGenerator> anns = (List)defaults.get(SequenceGenerator.class);
        if (anns != null)
        {
            for (SequenceGenerator ann : anns)
            {
                IdGenerator idGen = buildIdGenerator(ann, mappings);
                if (idGen != null) mappings.addDefaultGenerator(idGen);
            }

        }

        List<TableGenerator> tg = (List)defaults.get(TableGenerator.class);
        if (anns != null)
        {
            for (TableGenerator ann : tg)
            {
                IdGenerator idGen = buildIdGenerator(ann, mappings);
                if (idGen != null) mappings.addDefaultGenerator(idGen);
            }

        }

        List<javax.persistence.NamedQuery> nq = (List)defaults.get(javax.persistence.NamedQuery.class);
        if (anns != null)
        {
            for (javax.persistence.NamedQuery ann : nq)
            {
                QueryBinder.bindQuery(ann, mappings, true);
            }

        }

        List<javax.persistence.NamedNativeQuery> nnq = (List)defaults.get(javax.persistence.NamedNativeQuery.class);
        if (anns != null)
        {
            for (javax.persistence.NamedNativeQuery ann : nnq)
            {
                QueryBinder.bindNativeQuery(ann, mappings, true);
            }

        }

        List<SqlResultSetMapping> ss = (List)defaults.get(SqlResultSetMapping.class);
        if (anns != null){ 
            for (SqlResultSetMapping ann : ss){
                QueryBinder.bindSqlResultsetMapping(ann, mappings, true);
            }
        }
    }

    public static void bindPackage(String packageName, ExtendedMappings mappings)
    {
        XPackage pckg = null;
        try
        {
            pckg = mappings.getReflectionManager().packageForName(packageName);
        }
        catch (ClassNotFoundException cnf)
        {
            log.warn("Package not found or wo package-info.java: " + packageName);
            return;
        }
        if (pckg.isAnnotationPresent(SequenceGenerator.class))
        {
            SequenceGenerator ann = (SequenceGenerator)pckg.getAnnotation(SequenceGenerator.class);
            IdGenerator idGen = buildIdGenerator(ann, mappings);
            mappings.addGenerator(idGen);
            log.debug("Add sequence generator with name: " + idGen.getName());
        }
        if (pckg.isAnnotationPresent(TableGenerator.class))
        {
            TableGenerator ann = (TableGenerator)pckg.getAnnotation(TableGenerator.class);
            IdGenerator idGen = buildIdGenerator(ann, mappings);
            mappings.addGenerator(idGen);
        }

        bindGenericGenerators(pckg, mappings);
        bindQueries(pckg, mappings);
        bindFilterDefs(pckg, mappings);
        bindTypeDefs(pckg, mappings);
        BinderHelper.bindAnyMetaDefs(pckg, mappings);
    }

    private static void bindGenericGenerators(XAnnotatedElement annotatedElement,
                                              ExtendedMappings mappings)
    {
        GenericGenerator defAnn = (GenericGenerator)annotatedElement.getAnnotation(GenericGenerator.class);
        GenericGenerators defsAnn = (GenericGenerators)annotatedElement.getAnnotation(GenericGenerators.class);
        if (defAnn != null)
        {
            bindGenericGenerator(defAnn, mappings);
        }
        if (defsAnn != null) for (GenericGenerator def : defsAnn.value())
            bindGenericGenerator(def, mappings);
    }

    private static void bindGenericGenerator(GenericGenerator def, ExtendedMappings mappings)
    {
        IdGenerator idGen = buildIdGenerator(def, mappings);
        mappings.addGenerator(idGen);
    }

    private static void bindQueries(XAnnotatedElement annotatedElement, ExtendedMappings mappings)
    {
        SqlResultSetMapping ann = (SqlResultSetMapping)annotatedElement.getAnnotation(SqlResultSetMapping.class);
        QueryBinder.bindSqlResultsetMapping(ann, mappings, false);

        SqlResultSetMappings ssm = (SqlResultSetMappings)annotatedElement.getAnnotation(SqlResultSetMappings.class);
        if (ssm != null)
        {
            for (SqlResultSetMapping current : ssm.value())
            {
                QueryBinder.bindSqlResultsetMapping(current, mappings, false);
            }

        }

        javax.persistence.NamedQuery nq = (javax.persistence.NamedQuery)annotatedElement.getAnnotation(javax.persistence.NamedQuery.class);
        QueryBinder.bindQuery(nq, mappings, false);

        org.hibernate.annotations.NamedQuery hibernateNq = (org.hibernate.annotations.NamedQuery)annotatedElement.getAnnotation(org.hibernate.annotations.NamedQuery.class);

        QueryBinder.bindQuery(hibernateNq, mappings);

        javax.persistence.NamedQueries namedQueries = (javax.persistence.NamedQueries)annotatedElement.getAnnotation(javax.persistence.NamedQueries.class);
        QueryBinder.bindQueries(namedQueries, mappings, false);

        org.hibernate.annotations.NamedQueries hibernateNamedQueries = (org.hibernate.annotations.NamedQueries)annotatedElement.getAnnotation(org.hibernate.annotations.NamedQueries.class);

        QueryBinder.bindQueries(hibernateNamedQueries, mappings);

        javax.persistence.NamedNativeQuery nnq = (javax.persistence.NamedNativeQuery)annotatedElement.getAnnotation(javax.persistence.NamedNativeQuery.class);
        QueryBinder.bindNativeQuery(nnq, mappings, false);

        org.hibernate.annotations.NamedNativeQuery hnnq = (org.hibernate.annotations.NamedNativeQuery)annotatedElement.getAnnotation(org.hibernate.annotations.NamedNativeQuery.class);

        QueryBinder.bindNativeQuery(hnnq, mappings);

        javax.persistence.NamedNativeQueries nnqueries = (javax.persistence.NamedNativeQueries)annotatedElement.getAnnotation(javax.persistence.NamedNativeQueries.class);
        QueryBinder.bindNativeQueries(nnqueries, mappings, false);

        org.hibernate.annotations.NamedNativeQueries hnnqeries = (org.hibernate.annotations.NamedNativeQueries)annotatedElement.getAnnotation(org.hibernate.annotations.NamedNativeQueries.class);

        QueryBinder.bindNativeQueries(hnnqeries, mappings);
    }

    private static IdGenerator buildIdGenerator(Annotation ann, Mappings mappings)
    {
        IdGenerator idGen = new IdGenerator();
        if (mappings.getSchemaName() != null)
        {
            idGen.addParam("schema", mappings.getSchemaName());
        }
        if (mappings.getCatalogName() != null)
        {
            idGen.addParam("catalog", mappings.getCatalogName());
        }
        if (ann == null)
        {
            idGen = null;
        }
        else if (ann instanceof TableGenerator)
        {
            TableGenerator tabGen = (TableGenerator)ann;
            idGen.setName(tabGen.name());
            idGen.setIdentifierGeneratorStrategy(MultipleHiLoPerTableGenerator.class.getName());

            if (!(BinderHelper.isDefault(tabGen.table())))
            {
                idGen.addParam("table", tabGen.table());
            }
            if (!(BinderHelper.isDefault(tabGen.catalog())))
            {
                idGen.addParam("catalog", tabGen.catalog());
            }
            if (!(BinderHelper.isDefault(tabGen.schema())))
            {
                idGen.addParam("schema", tabGen.schema());
            }

            if (!(BinderHelper.isDefault(tabGen.pkColumnName())))
            {
                idGen.addParam("primary_key_column", tabGen.pkColumnName());
            }
            if (!(BinderHelper.isDefault(tabGen.valueColumnName())))
            {
                idGen.addParam("value_column", tabGen.valueColumnName());
            }
            if (!(BinderHelper.isDefault(tabGen.pkColumnValue())))
            {
                idGen.addParam("primary_key_value", tabGen.pkColumnValue());
            }
            idGen.addParam("max_lo", String.valueOf(tabGen.allocationSize() - 1));
            log.debug("Add table generator with name: " + idGen.getName());
        }
        else if (ann instanceof SequenceGenerator)
        {
            SequenceGenerator seqGen = (SequenceGenerator)ann;
            idGen.setName(seqGen.name());
            idGen.setIdentifierGeneratorStrategy("seqhilo");

            if (!(BinderHelper.isDefault(seqGen.sequenceName())))
            {
                idGen.addParam("sequence", seqGen.sequenceName());
            }

            if (seqGen.initialValue() != 1)
            {
                log.warn("Hibernate does not support SequenceGenerator.initialValue()");
            }

            idGen.addParam("max_lo", String.valueOf(seqGen.allocationSize() - 1));
            log.debug("Add sequence generator with name: " + idGen.getName());
        }
        else if (ann instanceof GenericGenerator)
        {
            GenericGenerator genGen = (GenericGenerator)ann;
            idGen.setName(genGen.name());
            idGen.setIdentifierGeneratorStrategy(genGen.strategy());
            Parameter[] params = genGen.parameters();
            for (Parameter parameter : params)
            {
                idGen.addParam(parameter.name(), parameter.value());
            }
            log.debug("Add generic generator with name: " + idGen.getName());
        }
        else
        {
            throw new AssertionFailure("Unknown Generator annotation: " + ann);
        }
        return idGen;
    }

    public static void bindClass(XClass clazzToProcess,
                                 Map<XClass, InheritanceState> inheritanceStatePerClass,
                                 ExtendedMappings mappings)
        throws MappingException
    {
        InheritanceState inheritanceState = (InheritanceState)inheritanceStatePerClass.get(clazzToProcess);
        AnnotatedClassType classType = mappings.getClassType(clazzToProcess);
        if ((AnnotatedClassType.EMBEDDABLE_SUPERCLASS.equals(classType))
            || (AnnotatedClassType.NONE.equals(classType))
            || (AnnotatedClassType.EMBEDDABLE.equals(classType)))
        {
            if ((AnnotatedClassType.NONE.equals(classType))
                && (clazzToProcess.isAnnotationPresent(org.hibernate.annotations.Entity.class)))
            {
                log.warn("Class annotated @org.hibernate.annotations.Entity but not javax.persistence.Entity (most likely a user error): "
                         + clazzToProcess.getName());
            }

            return;
        }
        if (!(classType.equals(AnnotatedClassType.ENTITY)))
        {
            throw new AnnotationException(
                "Annotated class should have a @javax.persistence.Entity, @javax.persistence.Embeddable or @javax.persistence.EmbeddedSuperclass annotation: "
                    + clazzToProcess.getName());
        }

        XAnnotatedElement annotatedClass = clazzToProcess;
        if (log.isInfoEnabled())
            log.info("Binding entity from annotated class: " + clazzToProcess.getName());
        InheritanceState superEntityState = InheritanceState.getSuperEntityInheritanceState(
            clazzToProcess, inheritanceStatePerClass, mappings.getReflectionManager());

        PersistentClass superEntity = (superEntityState != null) ? mappings.getClass(superEntityState.clazz.getName()) : null;

        if ((superEntity == null) && (inheritanceState.hasParents))
        {
            throw new AssertionFailure("Subclass has to be binded after it's mother class: "
                                       + superEntityState.clazz.getName());
        }

        bindQueries(annotatedClass, mappings);
        bindFilterDefs(annotatedClass, mappings);
        bindTypeDefs(annotatedClass, mappings);
        BinderHelper.bindAnyMetaDefs(annotatedClass, mappings);

        String schema = "";
        String table = "";
        String catalog = "";
        String discrimValue = null;
        List uniqueConstraints = new ArrayList();
        Ejb3DiscriminatorColumn discriminatorColumn = null;
        Ejb3JoinColumn[] inheritanceJoinedColumns = null;

        if (annotatedClass.isAnnotationPresent(javax.persistence.Table.class))
        {
            javax.persistence.Table tabAnn = (javax.persistence.Table)annotatedClass.getAnnotation(javax.persistence.Table.class);
            table = tabAnn.name();
            schema = tabAnn.schema();
            catalog = tabAnn.catalog();
            uniqueConstraints = TableBinder.buildUniqueConstraints(tabAnn.uniqueConstraints());
        }
        boolean hasJoinedColumns = (inheritanceState.hasParents)
                                   && (InheritanceType.JOINED.equals(inheritanceState.type));

        if (hasJoinedColumns)
        {
            PrimaryKeyJoinColumns jcsAnn = (PrimaryKeyJoinColumns)annotatedClass.getAnnotation(PrimaryKeyJoinColumns.class);
            boolean explicitInheritanceJoinedColumns = (jcsAnn != null)
                                                       && (jcsAnn.value().length != 0);
            if (explicitInheritanceJoinedColumns)
            {
                int nbrOfInhJoinedColumns = jcsAnn.value().length;

                inheritanceJoinedColumns = new Ejb3JoinColumn[nbrOfInhJoinedColumns];
                for (int colIndex = 0; colIndex < nbrOfInhJoinedColumns; ++colIndex)
                {
                    PrimaryKeyJoinColumn jcAnn = jcsAnn.value()[colIndex];
                    inheritanceJoinedColumns[colIndex] = Ejb3JoinColumn.buildJoinColumn(jcAnn,
                        null, superEntity.getIdentifier(), (Map)null, (PropertyHolder)null,
                        mappings);
                }

            }
            else
            {
                PrimaryKeyJoinColumn jcAnn = (PrimaryKeyJoinColumn)annotatedClass.getAnnotation(PrimaryKeyJoinColumn.class);
                inheritanceJoinedColumns = new Ejb3JoinColumn[1];
                inheritanceJoinedColumns[0] = Ejb3JoinColumn.buildJoinColumn(jcAnn, null,
                    superEntity.getIdentifier(), (Map)null, (PropertyHolder)null, mappings);
            }

            log.debug("Subclass joined column(s) created");
        }
        else if ((annotatedClass.isAnnotationPresent(PrimaryKeyJoinColumns.class))
                 || (annotatedClass.isAnnotationPresent(PrimaryKeyJoinColumn.class)))
        {
            log.warn("Root entity should not hold an PrimaryKeyJoinColum(s), will be ignored");
        }

        if (InheritanceType.SINGLE_TABLE.equals(inheritanceState.type))
        {
            DiscriminatorColumn discAnn = (DiscriminatorColumn)annotatedClass.getAnnotation(DiscriminatorColumn.class);

            DiscriminatorType discriminatorType = (discAnn != null) ? discAnn.discriminatorType() : DiscriminatorType.STRING;

            DiscriminatorFormula discFormulaAnn = (DiscriminatorFormula)annotatedClass.getAnnotation(DiscriminatorFormula.class);

            if (!(inheritanceState.hasParents))
            {
                discriminatorColumn = Ejb3DiscriminatorColumn.buildDiscriminatorColumn(
                    discriminatorType, discAnn, discFormulaAnn, mappings);
            }

            if ((discAnn != null) && (inheritanceState.hasParents))
            {
                log.warn("Discriminator column has to be defined in the root entity, it will be ignored in subclass: "
                         + clazzToProcess.getName());
            }

            discrimValue = (annotatedClass.isAnnotationPresent(DiscriminatorValue.class)) ? ((DiscriminatorValue)annotatedClass.getAnnotation(DiscriminatorValue.class)).value() : null;
        }
        PersistentClass persistentClass;
        if (!(inheritanceState.hasParents))
        {
            persistentClass = new RootClass();
        }
        else
        {
            if (InheritanceType.SINGLE_TABLE.equals(inheritanceState.type))
            {
                persistentClass = new SingleTableSubclass(superEntity);
            }
            else
            {
                if (InheritanceType.JOINED.equals(inheritanceState.type))
                {
                    persistentClass = new JoinedSubclass(superEntity);
                }
                else
                {
                    if (InheritanceType.TABLE_PER_CLASS.equals(inheritanceState.type))
                    {
                        persistentClass = new UnionSubclass(superEntity);
                    }
                    else
                        throw new AssertionFailure("Unknown inheritance type: "
                                                   + inheritanceState.type);
                }
            }
        }
        Proxy proxyAnn = (Proxy)annotatedClass.getAnnotation(Proxy.class);
        BatchSize sizeAnn = (BatchSize)annotatedClass.getAnnotation(BatchSize.class);
        Where whereAnn = (Where)annotatedClass.getAnnotation(Where.class);
        javax.persistence.Entity entityAnn = (javax.persistence.Entity)annotatedClass.getAnnotation(javax.persistence.Entity.class);
        org.hibernate.annotations.Entity hibEntityAnn = (org.hibernate.annotations.Entity)annotatedClass.getAnnotation(org.hibernate.annotations.Entity.class);

        Cache cacheAnn = (Cache)annotatedClass.getAnnotation(Cache.class);

        EntityBinder entityBinder = new EntityBinder(entityAnn, hibEntityAnn, clazzToProcess,
            persistentClass, mappings);

        entityBinder.setDiscriminatorValue(discrimValue);
        entityBinder.setBatchSize(sizeAnn);
        entityBinder.setProxy(proxyAnn);
        entityBinder.setWhere(whereAnn);
        entityBinder.setCache(cacheAnn);
        entityBinder.setInheritanceState(inheritanceState);
        Filter filterAnn = (Filter)annotatedClass.getAnnotation(Filter.class);
        if (filterAnn != null)
        {
            entityBinder.addFilter(filterAnn.name(), filterAnn.condition());
        }
        Filters filtersAnn = (Filters)annotatedClass.getAnnotation(Filters.class);
        if (filtersAnn != null)
        {
            for (Filter filter : filtersAnn.value())
            {
                entityBinder.addFilter(filter.name(), filter.condition());
            }
        }
        entityBinder.bindEntity();

        if (inheritanceState.hasTable())
        {
            Check checkAnn = (Check)annotatedClass.getAnnotation(Check.class);
            String constraints = (checkAnn == null) ? null : checkAnn.constraints();

            entityBinder.bindTable(schema, catalog, table, uniqueConstraints, constraints,
                (inheritanceState.hasDenormalizedTable()) ? superEntity.getTable() : null);
        }
        else if (annotatedClass.isAnnotationPresent(javax.persistence.Table.class))
        {
            log.warn("Illegal use of @Table in a subclass of a SINGLE_TABLE hierarchy: "
                     + clazzToProcess.getName());
        }

        PropertyHolder propertyHolder = PropertyHolderBuilder.buildPropertyHolder(clazzToProcess,
            persistentClass, entityBinder, mappings);

        SecondaryTable secTabAnn = (SecondaryTable)annotatedClass.getAnnotation(SecondaryTable.class);

        SecondaryTables secTabsAnn = (SecondaryTables)annotatedClass.getAnnotation(SecondaryTables.class);

        entityBinder.firstLevelSecondaryTablesBinding(secTabAnn, secTabsAnn);

        OnDelete onDeleteAnn = (OnDelete)annotatedClass.getAnnotation(OnDelete.class);
        boolean onDeleteAppropriate = false;
        if ((InheritanceType.JOINED.equals(inheritanceState.type))
            && (inheritanceState.hasParents))
        {
            onDeleteAppropriate = true;
            JoinedSubclass jsc = (JoinedSubclass)persistentClass;
            if (persistentClass.getEntityPersisterClass() == null)
            {
                persistentClass.getRootClass().setEntityPersisterClass(
                    JoinedSubclassEntityPersister.class);
            }
            SimpleValue key = new DependantValue(jsc.getTable(), jsc.getIdentifier());
            jsc.setKey(key);
            ForeignKey fk = (ForeignKey)annotatedClass.getAnnotation(ForeignKey.class);
            if ((fk != null) && (!(BinderHelper.isDefault(fk.name()))))
            {
                key.setForeignKeyName(fk.name());
            }
            if (onDeleteAnn != null)
            {
                key.setCascadeDeleteEnabled(OnDeleteAction.CASCADE.equals(onDeleteAnn.action()));
            }
            else
            {
                key.setCascadeDeleteEnabled(false);
            }

            SecondPass sp = new JoinedSubclassFkSecondPass(jsc, inheritanceJoinedColumns, key,
                mappings);
            mappings.addSecondPass(sp);
            mappings.addSecondPass(new CreateKeySecondPass(jsc));
        }
        else if (InheritanceType.SINGLE_TABLE.equals(inheritanceState.type))
        {
            if (inheritanceState.hasParents)
            {
                if (persistentClass.getEntityPersisterClass() == null)
                {
                    persistentClass.getRootClass().setEntityPersisterClass(
                        SingleTableEntityPersister.class);
                }

            }
            else if ((inheritanceState.hasSons) || (!(discriminatorColumn.isImplicit())))
            {
                bindDiscriminatorToPersistentClass((RootClass)persistentClass,
                    discriminatorColumn, entityBinder.getSecondaryTables(), propertyHolder);

                entityBinder.bindDiscriminatorValue();
            }

        }
        else if ((InheritanceType.TABLE_PER_CLASS.equals(inheritanceState.type))
                 && (inheritanceState.hasParents)
                 && (persistentClass.getEntityPersisterClass() == null))
        {
            persistentClass.getRootClass().setEntityPersisterClass(
                UnionSubclassEntityPersister.class);
        }

        if ((onDeleteAnn != null) && (!(onDeleteAppropriate)))
        {
            log.warn("Inapropriate use of @OnDelete on entity, annotation ignored: "
                     + propertyHolder.getEntityName());
        }

        HashMap classGenerators = buildLocalGenerators(annotatedClass, mappings);

        List<PropertyData> elements = getElementsToProcess(clazzToProcess, inheritanceStatePerClass,
            propertyHolder, entityBinder, mappings);

        if (elements == null)
        {
            throw new AnnotationException("No identifier specified for entity: "
                                          + propertyHolder.getEntityName());
        }
        boolean subclassAndSingleTableStrategy = (inheritanceState.type == InheritanceType.SINGLE_TABLE)
                                                 && (inheritanceState.hasParents);

        Set idProperties = new HashSet();
        IdClass idClass = null;
        if (!(inheritanceState.hasParents))
        {
            XClass current = inheritanceState.clazz;
            InheritanceState state = inheritanceState;
            do
            {
                current = state.clazz;
                if (current.isAnnotationPresent(IdClass.class))
                {
                    idClass = (IdClass)current.getAnnotation(IdClass.class);
                    break;
                }
                state = InheritanceState.getSuperclassInheritanceState(current,
                    inheritanceStatePerClass, mappings.getReflectionManager());
            }

            while (state != null);
        }
        if (idClass != null)
        {
            XClass compositeClass = mappings.getReflectionManager().toXClass(idClass.value());
            boolean isComponent = true;
            boolean propertyAnnotated = entityBinder.isPropertyAnnotated(compositeClass);
            String propertyAccessor = entityBinder.getPropertyAccessor(compositeClass);
            String generatorType = "assigned";
            String generator = "";
            PropertyData inferredData = new PropertyPreloadedData(
                entityBinder.getPropertyAccessor(), "id", compositeClass);

            HashMap localGenerators = new HashMap();
            boolean ignoreIdAnnotations = entityBinder.isIgnoreIdAnnotations();
            entityBinder.setIgnoreIdAnnotations(true);
            bindId(generatorType, generator, inferredData, null, propertyHolder, localGenerators,
                isComponent, propertyAnnotated, propertyAccessor, entityBinder, null, true, false,
                mappings);

            inferredData = new PropertyPreloadedData(propertyAccessor, "_identifierMapper",
                compositeClass);

            Component mapper = fillComponent(propertyHolder, inferredData, propertyAnnotated,
                propertyAccessor, false, entityBinder, true, true, false, mappings);

            entityBinder.setIgnoreIdAnnotations(ignoreIdAnnotations);
            persistentClass.setIdentifierMapper(mapper);
            Property property = new Property();
            property.setName("_identifierMapper");
            property.setNodeName("id");
            property.setUpdateable(false);
            property.setInsertable(false);
            property.setValue(mapper);
            property.setPropertyAccessorName("embedded");
            persistentClass.addProperty(property);
            entityBinder.setIgnoreIdAnnotations(true);

            Iterator properties = mapper.getPropertyIterator();
            while (properties.hasNext())
            {
                idProperties.add(((Property)properties.next()).getName());
            }
        }
        Set<String> missingIdProperties = new HashSet<String>(idProperties);
        for (PropertyData propertyAnnotatedElement : elements)
        {
            String propertyName = propertyAnnotatedElement.getPropertyName();
            if (!(idProperties.contains(propertyName)))
            {
                processElementAnnotations(
                    propertyHolder,
                    (subclassAndSingleTableStrategy) ? Nullability.FORCED_NULL : Nullability.NO_CONSTRAINT,
                    propertyAnnotatedElement.getProperty(), propertyAnnotatedElement,
                    classGenerators, entityBinder, false, false, false, mappings);
            }
            else
            {
                missingIdProperties.remove(propertyName);
            }
        }

        if (missingIdProperties.size() != 0)
        {
            StringBuilder missings = new StringBuilder();
            for (String property : missingIdProperties)
            {
                missings.append(property).append(", ");
            }
            throw new AnnotationException("Unable to find properties ("
                                          + missings.substring(0, missings.length() - 2)
                                          + ") in entity annotated with @IdClass:"
                                          + persistentClass.getEntityName());
        }

        if (!(inheritanceState.hasParents))
        {
            RootClass rootClass = (RootClass)persistentClass;
            mappings.addSecondPass(new CreateKeySecondPass(rootClass));
        }
        else
        {
            superEntity.addSubclass((Subclass)persistentClass);
        }

        mappings.addClass(persistentClass);

        mappings.addSecondPass(new SecondaryTableSecondPass(entityBinder, propertyHolder,
            annotatedClass));

        entityBinder.processComplementaryTableDefinitions((org.hibernate.annotations.Table)annotatedClass.getAnnotation(org.hibernate.annotations.Table.class));
        entityBinder.processComplementaryTableDefinitions((Tables)annotatedClass.getAnnotation(Tables.class));
        
        //add by wanghuawen for comment
        CommentBinder.bindTableComment(clazzToProcess, persistentClass);//绑定表名注释
        
    }

    private static List<PropertyData> getElementsToProcess(XClass clazzToProcess,
                                                           Map<XClass, InheritanceState> inheritanceStatePerClass,
                                                           PropertyHolder propertyHolder,
                                                           EntityBinder entityBinder,
                                                           ExtendedMappings mappings)
    {
        InheritanceState inheritanceState = (InheritanceState)inheritanceStatePerClass.get(clazzToProcess);
        List classesToProcess = orderClassesToBeProcessed(clazzToProcess,
            inheritanceStatePerClass, inheritanceState, mappings);

        List elements = new ArrayList();
        int deep = classesToProcess.size();
        boolean hasIdentifier = false;

        assert (!(inheritanceState.isEmbeddableSuperclass));
        Boolean isExplicitPropertyAnnotated = null;
        String explicitAccessType = null;
        if (inheritanceState.hasParents)
        {
            InheritanceState superEntityState = InheritanceState.getSuperEntityInheritanceState(
                clazzToProcess, inheritanceStatePerClass, mappings.getReflectionManager());

            isExplicitPropertyAnnotated = (superEntityState != null) ? superEntityState.isPropertyAnnotated : null;

            explicitAccessType = (superEntityState != null) ? superEntityState.accessType : null;
        }
        else
        {
            AccessType access = (AccessType)clazzToProcess.getAnnotation(AccessType.class);
            explicitAccessType = (access != null) ? access.value() : null;

            if ("property".equals(explicitAccessType))
            {
                isExplicitPropertyAnnotated = Boolean.TRUE;
            }
            else if ("field".equals(explicitAccessType))
            {
                isExplicitPropertyAnnotated = Boolean.FALSE;
            }
        }
        Boolean isPropertyAnnotated = (isExplicitPropertyAnnotated == null) ? Boolean.TRUE : isExplicitPropertyAnnotated;

        String accessType = (explicitAccessType != null) ? explicitAccessType : "property";

        AnnotationException exceptionWhileWalkingElements = null;
        try
        {
            for (int index = 0; index < deep; ++index)
            {
                XClass clazz = (XClass)classesToProcess.get(index);

                boolean currentHasIdentifier = addElementsOfAClass(elements, propertyHolder,
                    isPropertyAnnotated.booleanValue(), accessType, clazz, mappings);

                hasIdentifier = (hasIdentifier) || (currentHasIdentifier);
            }
        }
        catch (AnnotationException e)
        {
            exceptionWhileWalkingElements = e;
        }

        if ((!(hasIdentifier)) && (!(inheritanceState.hasParents)))
        {
            if (isExplicitPropertyAnnotated != null)
            {
                if (exceptionWhileWalkingElements != null) throw exceptionWhileWalkingElements;
                return null;
            }
            isPropertyAnnotated = Boolean.valueOf(!(isPropertyAnnotated.booleanValue()));
            accessType = "field";
            elements.clear();
            for (int index = 0; index < deep; ++index)
            {
                XClass clazz = (XClass)classesToProcess.get(index);
                boolean currentHasIdentifier = addElementsOfAClass(elements, propertyHolder,
                    isPropertyAnnotated.booleanValue(), accessType, clazz, mappings);

                hasIdentifier = (hasIdentifier) || (currentHasIdentifier);
            }

        }

        if ((!(hasIdentifier)) && (exceptionWhileWalkingElements != null))
            throw exceptionWhileWalkingElements;

        entityBinder.setPropertyAnnotated(isPropertyAnnotated.booleanValue());
        entityBinder.setPropertyAccessor(accessType);
        inheritanceState.isPropertyAnnotated = isPropertyAnnotated;
        inheritanceState.accessType = accessType;
        return (((hasIdentifier) || (inheritanceState.hasParents)) ? elements : null);
    }

    private static List<XClass> orderClassesToBeProcessed(XClass annotatedClass,
                                                          Map<XClass, InheritanceState> inheritanceStatePerClass,
                                                          InheritanceState inheritanceState,
                                                          ExtendedMappings mappings)
    {
        List classesToProcess = new ArrayList();
        XClass currentClassInHierarchy = annotatedClass;
        InheritanceState superclassState;
        do
        {
            classesToProcess.add(0, currentClassInHierarchy);
            XClass superClass = currentClassInHierarchy;
            do
            {
                superClass = superClass.getSuperclass();
                superclassState = (InheritanceState)inheritanceStatePerClass.get(superClass);
            }

            while ((superClass != null)
                   && (!(mappings.getReflectionManager().equals(superClass, Object.class)))
                   && (superclassState == null));

            currentClassInHierarchy = superClass;
        }
        while ((superclassState != null) && (superclassState.isEmbeddableSuperclass));

        return classesToProcess;
    }

    private static void bindFilterDefs(XAnnotatedElement annotatedElement,
                                       ExtendedMappings mappings)
    {
        FilterDef defAnn = (FilterDef)annotatedElement.getAnnotation(FilterDef.class);
        FilterDefs defsAnn = (FilterDefs)annotatedElement.getAnnotation(FilterDefs.class);
        if (defAnn != null)
        {
            bindFilterDef(defAnn, mappings);
        }
        if (defsAnn != null) for (FilterDef def : defsAnn.value())
            bindFilterDef(def, mappings);
    }

    private static void bindFilterDef(FilterDef defAnn, ExtendedMappings mappings)
    {
        Map params = new HashMap();
        for (ParamDef param : defAnn.parameters())
        {
            params.put(param.name(), TypeFactory.heuristicType(param.type()));
        }
        FilterDefinition def = new FilterDefinition(defAnn.name(), defAnn.defaultCondition(),
            params);
        if (log.isInfoEnabled()) log.info("Binding filter definition: " + def.getFilterName());
        mappings.addFilterDefinition(def);
    }

    private static void bindTypeDefs(XAnnotatedElement annotatedElement, ExtendedMappings mappings)
    {
        TypeDef defAnn = (TypeDef)annotatedElement.getAnnotation(TypeDef.class);
        TypeDefs defsAnn = (TypeDefs)annotatedElement.getAnnotation(TypeDefs.class);
        if (defAnn != null)
        {
            bindTypeDef(defAnn, mappings);
        }
        if (defsAnn != null) for (TypeDef def : defsAnn.value())
            bindTypeDef(def, mappings);
    }

    private static void bindTypeDef(TypeDef defAnn, ExtendedMappings mappings)
    {
        Properties params = new Properties();
        for (Parameter param : defAnn.parameters())
        {
            params.setProperty(param.name(), param.value());
        }
        if (log.isInfoEnabled()) log.info("Binding type definition: " + defAnn.name());
        mappings.addTypeDef(defAnn.name(), defAnn.typeClass().getName(), params);
    }

    private static void bindDiscriminatorToPersistentClass(RootClass rootClass,
                                                           Ejb3DiscriminatorColumn discriminatorColumn,
                                                           Map<String, Join> secondaryTables,
                                                           PropertyHolder propertyHolder)
    {
        if (rootClass.getDiscriminator() == null)
        {
            if (discriminatorColumn == null)
            {
                throw new AssertionFailure("discriminator column should have been built");
            }
            discriminatorColumn.setJoins(secondaryTables);
            discriminatorColumn.setPropertyHolder(propertyHolder);
            SimpleValue discrim = new SimpleValue(rootClass.getTable());
            rootClass.setDiscriminator(discrim);
            discriminatorColumn.linkWithValue(discrim);
            discrim.setTypeName(discriminatorColumn.getDiscriminatorTypeName());
            rootClass.setPolymorphic(true);
            log.debug("Setting discriminator for entity " + rootClass.getEntityName());
        }
    }

    private static boolean addElementsOfAClass(List<PropertyData> elements,
                                               PropertyHolder propertyHolder,
                                               boolean isPropertyAnnotated,
                                               String propertyAccessor, XClass annotatedClass,
                                               ExtendedMappings mappings)
    {
        boolean hasIdentifier = false;
        AccessType access = (AccessType)annotatedClass.getAnnotation(AccessType.class);
        String localPropertyAccessor = (access != null) ? access.value() : null;

        String accessType = null;
        if (("property".equals(localPropertyAccessor)) || ("field".equals(localPropertyAccessor)))
        {
            accessType = localPropertyAccessor;
        }
        else
        {
            if (localPropertyAccessor == null)
            {
                localPropertyAccessor = propertyAccessor;
            }

            if (isPropertyAnnotated)
            {
                accessType = "property";
            }
            else
            {
                accessType = "field";
            }
        }

        log.debug("Processing " + propertyHolder.getEntityName() + " " + accessType
                  + " annotation");
        List<XProperty> properties = annotatedClass.getDeclaredProperties(accessType);

        Collections.sort(properties, new Comparator()
        {
            @Override
            public int compare(Object o1, Object o2)
            {
                return ((XProperty)o1).getName().compareTo(((XProperty)o2).getName());
            }
        });
        
        
        for (XProperty p : properties)
        {
            if ((!(p.isTypeResolved())) && (!(discoverTypeWithoutReflection(p)))
                && (!(mustBeSkipped(p, mappings))))
            {
                throw new AnnotationException(
                    "Property "
                        + StringHelper.qualify(propertyHolder.getEntityName(), p.getName())
                        + " has an unbound type and no explicit target entity. Resolve this Generic usage issue"
                        + " or set an explicit target attribute (eg @OneToMany(target=) or use an explicit @Type");
            }

            boolean currentHasIdentifier = addProperty(p, elements, localPropertyAccessor,
                mappings);
            hasIdentifier = (hasIdentifier) || (currentHasIdentifier);
        }
        return hasIdentifier;
    }

    private static boolean discoverTypeWithoutReflection(XProperty p)
    {
        if ((p.isAnnotationPresent(OneToOne.class))
            && (!(((OneToOne)p.getAnnotation(OneToOne.class)).targetEntity().equals(Void.TYPE))))
        {
            return true;
        }
        if ((p.isAnnotationPresent(OneToMany.class))
            && (!(((OneToMany)p.getAnnotation(OneToMany.class)).targetEntity().equals(Void.TYPE))))
        {
            return true;
        }
        if ((p.isAnnotationPresent(javax.persistence.ManyToOne.class))
            && (!(((javax.persistence.ManyToOne)p.getAnnotation(javax.persistence.ManyToOne.class)).targetEntity().equals(Void.TYPE))))
        {
            return true;
        }
        if ((p.isAnnotationPresent(ManyToMany.class))
            && (!(((ManyToMany)p.getAnnotation(ManyToMany.class)).targetEntity().equals(Void.TYPE))))
        {
            return true;
        }
        if (p.isAnnotationPresent(org.hibernate.annotations.Any.class))
        {
            return true;
        }
        if (p.isAnnotationPresent(ManyToAny.class))
        {
            if ((!(p.isCollection())) && (!(p.isArray())))
            {
                throw new AnnotationException(
                    "@ManyToAny used on a non collection non array property: " + p.getName());
            }
            return true;
        }
        if (p.isAnnotationPresent(Type.class))
        {
            return true;
        }

        return (p.isAnnotationPresent(Target.class));
    }

    private static boolean addProperty(XProperty property, List<PropertyData> annElts,
                                       String propertyAccessor, ExtendedMappings mappings)
    {
        boolean hasIdentifier = false;
        PropertyData propertyAnnotatedElement = new PropertyInferredData(property,
            propertyAccessor, mappings.getReflectionManager());

        if (!(mustBeSkipped(propertyAnnotatedElement.getProperty(), mappings)))
        {
            XAnnotatedElement element = propertyAnnotatedElement.getProperty();
            if ((element.isAnnotationPresent(Id.class))
                || (element.isAnnotationPresent(EmbeddedId.class)))
            {
                annElts.add(0, propertyAnnotatedElement);
                hasIdentifier = true;
            }
            else
            {
                annElts.add(propertyAnnotatedElement);
                hasIdentifier = false;
            }
        }
        return hasIdentifier;
    }

    private static boolean mustBeSkipped(XProperty property, ExtendedMappings mappings)
    {
        return ((property.isAnnotationPresent(Transient.class))
                || ("net.sf.cglib.transform.impl.InterceptFieldCallback".equals(property.getType().getName())) || ("org.hibernate.bytecode.javassist.FieldHandler".equals(property.getType().getName())));
    }

   
    private static void processElementAnnotations(PropertyHolder propertyHolder,
                                                  Nullability nullability, XProperty property,
                                                  PropertyData inferredData,
                                                  HashMap<String, IdGenerator> classGenerators,
                                                  EntityBinder entityBinder,
                                                  boolean isIdentifierMapper,
                                                  boolean isComponentEmbedded,
                                                  boolean inSecondPass, ExtendedMappings mappings)
        throws MappingException
    {
        Ejb3Column[] columns = null;
        Ejb3JoinColumn[] joinColumns = null;
        if (log.isDebugEnabled())
        {
            log.debug("Processing annotations of " + propertyHolder.getEntityName() + "."
                      + inferredData.getPropertyName());
        }

        if (property.isAnnotationPresent(Parent.class))
        {
            if (propertyHolder.isComponent())
            {
                propertyHolder.setParentProperty(property.getName());
            }
            else
            {
                throw new AnnotationException(
                    "@Parent cannot be applied outside an embeddable object: "
                        + StringHelper.qualify(propertyHolder.getPath(), property.getName()));
            }

            return;
        }

        JoinColumn[] anns = null;
        if (property.isAnnotationPresent(JoinColumn.class))
        {
            anns = new JoinColumn[] {(JoinColumn)property.getAnnotation(JoinColumn.class)};
        }
        else if (property.isAnnotationPresent(JoinColumns.class))
        {
            JoinColumns ann = (JoinColumns)property.getAnnotation(JoinColumns.class);
            anns = ann.value();
            int length = anns.length;
            if (length == 0)
            {
                throw new AnnotationException("Cannot bind an empty @JoinColumns");
            }
        }
        if (anns != null)
        {
            joinColumns = Ejb3JoinColumn.buildJoinColumns(anns, null,
                entityBinder.getSecondaryTables(), propertyHolder, inferredData.getPropertyName(),
                mappings);
        }

        if ((property.isAnnotationPresent(javax.persistence.Column.class))
            || (property.isAnnotationPresent(Formula.class)))
        {
            javax.persistence.Column ann = (javax.persistence.Column)property.getAnnotation(javax.persistence.Column.class);
            Formula formulaAnn = (Formula)property.getAnnotation(Formula.class);
            columns = Ejb3Column.buildColumnFromAnnotation(new javax.persistence.Column[] {ann},
                formulaAnn, nullability, propertyHolder, inferredData,
                entityBinder.getSecondaryTables(), mappings);
        }
        else if (property.isAnnotationPresent(Columns.class))
        {
            Columns anns1 = (Columns)property.getAnnotation(Columns.class);
            columns = Ejb3Column.buildColumnFromAnnotation(anns1.columns(), null, nullability,
                propertyHolder, inferredData, entityBinder.getSecondaryTables(), mappings);
        }

        if ((joinColumns == null)
            && (((property.isAnnotationPresent(javax.persistence.ManyToOne.class)) || (property.isAnnotationPresent(OneToOne.class)))))
        {
            if (property.isAnnotationPresent(JoinTable.class))
            {
                JoinTable joinTableAnn = (JoinTable)property.getAnnotation(JoinTable.class);
                joinColumns = Ejb3JoinColumn.buildJoinColumns(joinTableAnn.inverseJoinColumns(),
                    null, entityBinder.getSecondaryTables(), propertyHolder,
                    inferredData.getPropertyName(), mappings);

                if (StringHelper.isEmpty(joinTableAnn.name()))
                {
                    throw new AnnotationException(
                        "JoinTable.name() on a @ToOne association has to be explicit: "
                            + StringHelper.qualify(propertyHolder.getPath(),
                                inferredData.getPropertyName()));
                }

            }
            else
            {
                OneToOne oneToOneAnn = (OneToOne)property.getAnnotation(OneToOne.class);
                String mappedBy = (oneToOneAnn != null) ? oneToOneAnn.mappedBy() : null;

                joinColumns = Ejb3JoinColumn.buildJoinColumns((JoinColumn[])null, mappedBy,
                    entityBinder.getSecondaryTables(), propertyHolder,
                    inferredData.getPropertyName(), mappings);
            }

        }
        else if ((joinColumns == null)
                 && (((property.isAnnotationPresent(OneToMany.class)) || (property.isAnnotationPresent(CollectionOfElements.class)))))
        {
            OneToMany oneToMany = (OneToMany)property.getAnnotation(OneToMany.class);
            String mappedBy = (oneToMany != null) ? oneToMany.mappedBy() : "";

            joinColumns = Ejb3JoinColumn.buildJoinColumns((JoinColumn[])null, mappedBy,
                entityBinder.getSecondaryTables(), propertyHolder, inferredData.getPropertyName(),
                mappings);
        }
        else if ((joinColumns == null)
                 && (property.isAnnotationPresent(org.hibernate.annotations.Any.class)))
        {
            throw new AnnotationException("@Any requires an explicit @JoinColumn(s): "
                                          + StringHelper.qualify(propertyHolder.getPath(),
                                              property.getName()));
        }

        if ((columns == null) && (!(property.isAnnotationPresent(ManyToMany.class))))
        {
            columns = Ejb3Column.buildColumnFromAnnotation(null, null, nullability,
                propertyHolder, inferredData, entityBinder.getSecondaryTables(), mappings);
        }

        if (nullability == Nullability.FORCED_NOT_NULL)
        {
            for (Ejb3Column col : columns)
            {
                col.forceNotNull();
            }
        }

        XClass returnedClass = inferredData.getClassOrElement();
        if ((!(entityBinder.isIgnoreIdAnnotations()))
            && (((property.isAnnotationPresent(Id.class)) || (property.isAnnotationPresent(EmbeddedId.class)))))
        {
            if (isIdentifierMapper)
            {
                throw new AnnotationException(
                    "@IdClass class should not have @Id nor @EmbeddedId properties");
            }

            log.debug(inferredData.getPropertyName() + " is an id");

            HashMap localGenerators = (HashMap)classGenerators.clone();
            localGenerators.putAll(buildLocalGenerators(property, mappings));

            boolean isComponent = (returnedClass.isAnnotationPresent(Embeddable.class))
                                  || (property.isAnnotationPresent(EmbeddedId.class));

            boolean propertyAnnotated = entityBinder.isPropertyAnnotated(returnedClass);
            String propertyAccessor = entityBinder.getPropertyAccessor(returnedClass);

            GeneratedValue generatedValue = (GeneratedValue)property.getAnnotation(GeneratedValue.class);
            String generatorType = (generatedValue != null) ? generatorType(generatedValue.strategy()) : "assigned";

            String generator = (generatedValue != null) ? generatedValue.generator() : "";

            if (isComponent) generatorType = "assigned";
            Type typeAnn = (Type)property.getAnnotation(Type.class);
            bindId(generatorType, generator, inferredData, columns, propertyHolder,
                localGenerators, isComponent, propertyAnnotated, propertyAccessor, entityBinder,
                typeAnn, false, isIdentifierMapper, mappings);

            if (log.isDebugEnabled())
            {
                log.debug("Bind " + ((isComponent) ? "@EmbeddedId" : "@Id") + " on "
                          + inferredData.getPropertyName());
            }

        }
        else if (property.isAnnotationPresent(Version.class))
        {
            if (isIdentifierMapper)
            {
                throw new AnnotationException("@IdClass class should not have @Version property");
            }

            if (!(propertyHolder.getPersistentClass() instanceof RootClass))
            {
                throw new AnnotationException("Unable to define/override @Version on a subclass: "
                                              + propertyHolder.getEntityName());
            }

            if (!(propertyHolder.isEntity()))
            {
                throw new AnnotationException("Unable to define @Version on an embedded class: "
                                              + propertyHolder.getEntityName());
            }

            log.debug(inferredData.getPropertyName() + " is a version property");
            RootClass rootClass = (RootClass)propertyHolder.getPersistentClass();
            PropertyBinder propBinder = new PropertyBinder();
            propBinder.setName(inferredData.getPropertyName());
            propBinder.setReturnedClassName(inferredData.getTypeName());
            propBinder.setLazy(false);
            propBinder.setPropertyAccessorName(inferredData.getDefaultAccess());
            propBinder.setColumns(columns);
            propBinder.setHolder(propertyHolder);
            propBinder.setProperty(property);
            propBinder.setReturnedClass(inferredData.getPropertyClass());

            propBinder.setMappings(mappings);
            Property prop = propBinder.bind();
            rootClass.setVersion(prop);
            SimpleValue simpleValue = (SimpleValue)prop.getValue();
            if (!(simpleValue.isTypeSpecified())) simpleValue.setTypeName("integer");
            simpleValue.setNullValue("undefined");
            rootClass.setOptimisticLockMode(0);
            log.debug("Version name: " + rootClass.getVersion().getName() + ", unsavedValue: "
                      + ((SimpleValue)rootClass.getVersion().getValue()).getNullValue());
        }
        else if (property.isAnnotationPresent(javax.persistence.ManyToOne.class))
        {
            javax.persistence.ManyToOne ann = (javax.persistence.ManyToOne)property.getAnnotation(javax.persistence.ManyToOne.class);

            if ((property.isAnnotationPresent(javax.persistence.Column.class))
                || (property.isAnnotationPresent(Columns.class)))
            {
                throw new AnnotationException("@Column(s) not allowed on a @ManyToOne property: "
                                              + StringHelper.qualify(propertyHolder.getPath(),
                                                  inferredData.getPropertyName()));
            }

            Cascade hibernateCascade = (Cascade)property.getAnnotation(Cascade.class);
            NotFound notFound = (NotFound)property.getAnnotation(NotFound.class);
            boolean ignoreNotFound = (notFound != null)
                                     && (notFound.action().equals(NotFoundAction.IGNORE));
            OnDelete onDeleteAnn = (OnDelete)property.getAnnotation(OnDelete.class);
            boolean onDeleteCascade = (onDeleteAnn != null)
                                      && (OnDeleteAction.CASCADE.equals(onDeleteAnn.action()));
            JoinTable assocTable = (JoinTable)property.getAnnotation(JoinTable.class);
            if (assocTable != null)
            {
                Join join = propertyHolder.addJoin(assocTable, false);
                for (Ejb3JoinColumn joinColumn : joinColumns)
                {
                    joinColumn.setSecondaryTableName(join.getTable().getName());
                }
            }
            bindManyToOne(getCascadeStrategy(ann.cascade(), hibernateCascade), joinColumns,
                ann.optional(), ignoreNotFound, onDeleteCascade,
                mappings.getReflectionManager().toXClass(ann.targetEntity()), propertyHolder,
                inferredData, false, isIdentifierMapper, inSecondPass, mappings);
        }
        else if (property.isAnnotationPresent(OneToOne.class))
        {
            OneToOne ann = (OneToOne)property.getAnnotation(OneToOne.class);

            if ((property.isAnnotationPresent(javax.persistence.Column.class))
                || (property.isAnnotationPresent(Columns.class)))
            {
                throw new AnnotationException("@Column(s) not allowed on a @OneToOne property: "
                                              + StringHelper.qualify(propertyHolder.getPath(),
                                                  inferredData.getPropertyName()));
            }

            boolean trueOneToOne = (property.isAnnotationPresent(PrimaryKeyJoinColumn.class))
                                   || (property.isAnnotationPresent(PrimaryKeyJoinColumns.class));

            Cascade hibernateCascade = (Cascade)property.getAnnotation(Cascade.class);
            NotFound notFound = (NotFound)property.getAnnotation(NotFound.class);
            boolean ignoreNotFound = (notFound != null)
                                     && (notFound.action().equals(NotFoundAction.IGNORE));
            OnDelete onDeleteAnn = (OnDelete)property.getAnnotation(OnDelete.class);
            boolean onDeleteCascade = (onDeleteAnn != null)
                                      && (OnDeleteAction.CASCADE.equals(onDeleteAnn.action()));
            JoinTable assocTable = (JoinTable)property.getAnnotation(JoinTable.class);
            if (assocTable != null)
            {
                Join join = propertyHolder.addJoin(assocTable, false);
                for (Ejb3JoinColumn joinColumn : joinColumns)
                {
                    joinColumn.setSecondaryTableName(join.getTable().getName());
                }
            }
            bindOneToOne(getCascadeStrategy(ann.cascade(), hibernateCascade), joinColumns,
                ann.optional(), getFetchMode(ann.fetch()), ignoreNotFound, onDeleteCascade,
                mappings.getReflectionManager().toXClass(ann.targetEntity()), propertyHolder,
                inferredData, ann.mappedBy(), trueOneToOne, isIdentifierMapper, inSecondPass,
                mappings);
        }
        else if (property.isAnnotationPresent(org.hibernate.annotations.Any.class))
        {
            if ((property.isAnnotationPresent(javax.persistence.Column.class))
                || (property.isAnnotationPresent(Columns.class)))
            {
                throw new AnnotationException("@Column(s) not allowed on a @Any property: "
                                              + StringHelper.qualify(propertyHolder.getPath(),
                                                  inferredData.getPropertyName()));
            }

            Cascade hibernateCascade = (Cascade)property.getAnnotation(Cascade.class);
            NotFound notFound = (NotFound)property.getAnnotation(NotFound.class);
            @SuppressWarnings("unused")
            boolean ignoreNotFound = (notFound != null)
                                     && (notFound.action().equals(NotFoundAction.IGNORE));
            OnDelete onDeleteAnn = (OnDelete)property.getAnnotation(OnDelete.class);
            boolean onDeleteCascade = (onDeleteAnn != null)
                                      && (OnDeleteAction.CASCADE.equals(onDeleteAnn.action()));
            JoinTable assocTable = (JoinTable)property.getAnnotation(JoinTable.class);
            if (assocTable != null)
            {
                Join join = propertyHolder.addJoin(assocTable, false);
                for (Ejb3JoinColumn joinColumn : joinColumns)
                {
                    joinColumn.setSecondaryTableName(join.getTable().getName());
                }
            }
            bindAny(getCascadeStrategy(null, hibernateCascade), joinColumns, onDeleteCascade,
                nullability, propertyHolder, inferredData, entityBinder, isIdentifierMapper,
                mappings);
        }
        else if ((property.isAnnotationPresent(OneToMany.class))
                 || (property.isAnnotationPresent(ManyToMany.class))
                 || (property.isAnnotationPresent(CollectionOfElements.class))
                 || (property.isAnnotationPresent(ManyToAny.class)))
        {
            OneToMany oneToManyAnn = (OneToMany)property.getAnnotation(OneToMany.class);
            ManyToMany manyToManyAnn = (ManyToMany)property.getAnnotation(ManyToMany.class);
            CollectionOfElements collectionOfElementsAnn = (CollectionOfElements)property.getAnnotation(CollectionOfElements.class);
            org.hibernate.annotations.IndexColumn indexAnn = (org.hibernate.annotations.IndexColumn)property.getAnnotation(org.hibernate.annotations.IndexColumn.class);

            JoinTable assocTable = (JoinTable)property.getAnnotation(JoinTable.class);

            IndexColumn indexColumn = IndexColumn.buildColumnFromAnnotation(indexAnn,
                propertyHolder, inferredData, mappings);

            CollectionBinder collectionBinder = CollectionBinder.getCollectionBinder(
                propertyHolder.getEntityName(), property, !(indexColumn.isImplicit()));

            collectionBinder.setIndexColumn(indexColumn);
            javax.persistence.MapKey mapKeyAnn = (javax.persistence.MapKey)property.getAnnotation(javax.persistence.MapKey.class);
            collectionBinder.setMapKey(mapKeyAnn);
            collectionBinder.setPropertyName(inferredData.getPropertyName());
            BatchSize batchAnn = (BatchSize)property.getAnnotation(BatchSize.class);
            collectionBinder.setBatchSize(batchAnn);
            javax.persistence.OrderBy ejb3OrderByAnn = (javax.persistence.OrderBy)property.getAnnotation(javax.persistence.OrderBy.class);
            org.hibernate.annotations.OrderBy orderByAnn = (org.hibernate.annotations.OrderBy)property.getAnnotation(org.hibernate.annotations.OrderBy.class);
            collectionBinder.setEjb3OrderBy(ejb3OrderByAnn);
            collectionBinder.setSqlOrderBy(orderByAnn);
            Sort sortAnn = (Sort)property.getAnnotation(Sort.class);
            collectionBinder.setSort(sortAnn);
            Cache cachAnn = (Cache)property.getAnnotation(Cache.class);
            collectionBinder.setCache(cachAnn);
            collectionBinder.setPropertyHolder(propertyHolder);
            Cascade hibernateCascade = (Cascade)property.getAnnotation(Cascade.class);
            NotFound notFound = (NotFound)property.getAnnotation(NotFound.class);
            boolean ignoreNotFound = (notFound != null)
                                     && (notFound.action().equals(NotFoundAction.IGNORE));
            collectionBinder.setIgnoreNotFound(ignoreNotFound);
            collectionBinder.setCollectionType(inferredData.getProperty().getElementClass());
            collectionBinder.setMappings(mappings);
            collectionBinder.setPropertyAccessorName(inferredData.getDefaultAccess());

            Ejb3Column[] elementColumns = null;
            PropertyData virtualProperty = new WrappedInferredData(inferredData, "element");
            if ((property.isAnnotationPresent(javax.persistence.Column.class))
                || (property.isAnnotationPresent(Formula.class)))
            {
                javax.persistence.Column ann = (javax.persistence.Column)property.getAnnotation(javax.persistence.Column.class);
                Formula formulaAnn = (Formula)property.getAnnotation(Formula.class);
                elementColumns = Ejb3Column.buildColumnFromAnnotation(
                    new javax.persistence.Column[] {ann}, formulaAnn, nullability, propertyHolder,
                    virtualProperty, entityBinder.getSecondaryTables(), mappings);
            }
            else if (property.isAnnotationPresent(Columns.class))
            {
                Columns anns1 = (Columns)property.getAnnotation(Columns.class);
                elementColumns = Ejb3Column.buildColumnFromAnnotation(anns1.columns(), null,
                    nullability, propertyHolder, virtualProperty,
                    entityBinder.getSecondaryTables(), mappings);
            }
            else
            {
                elementColumns = Ejb3Column.buildColumnFromAnnotation(null, null, nullability,
                    propertyHolder, virtualProperty, entityBinder.getSecondaryTables(), mappings);
            }

            org.hibernate.annotations.MapKey hibMapKeyAnn = (org.hibernate.annotations.MapKey)property.getAnnotation(org.hibernate.annotations.MapKey.class);

            PropertyData mapKeyVirtualProperty = new WrappedInferredData(inferredData, "mapkey");
            Ejb3Column[] mapColumns = Ejb3Column.buildColumnFromAnnotation(
                ((hibMapKeyAnn != null) && (hibMapKeyAnn.columns().length > 0)) ? hibMapKeyAnn.columns() : null,
                null, Nullability.FORCED_NOT_NULL, propertyHolder, mapKeyVirtualProperty,
                entityBinder.getSecondaryTables(), mappings);

            collectionBinder.setMapKeyColumns(mapColumns);

            MapKeyManyToMany mapKeyManyToMany = (MapKeyManyToMany)property.getAnnotation(MapKeyManyToMany.class);
            Ejb3JoinColumn[] mapJoinColumns = Ejb3JoinColumn.buildJoinColumns(
                (mapKeyManyToMany != null) ? mapKeyManyToMany.joinColumns() : null, null,
                entityBinder.getSecondaryTables(), propertyHolder,
                mapKeyVirtualProperty.getPropertyName(), mappings);

            collectionBinder.setMapKeyManyToManyColumns(mapJoinColumns);

            collectionBinder.setEmbedded(property.isAnnotationPresent(Embedded.class));
            collectionBinder.setElementColumns(elementColumns);
            collectionBinder.setProperty(property);

            if ((oneToManyAnn != null) && (manyToManyAnn != null))
            {
                throw new AnnotationException(
                    "@OneToMany and @ManyToMany on the same property is not allowed: "
                        + propertyHolder.getEntityName() + "." + inferredData.getPropertyName());
            }

            String mappedBy = null;
            if (oneToManyAnn != null)
            {
                for (Ejb3JoinColumn column : joinColumns)
                {
                    if (column.isSecondary())
                    {
                        throw new NotYetImplementedException(
                            "Collections having FK in secondary table");
                    }
                }
                collectionBinder.setFkJoinColumns(joinColumns);
                mappedBy = oneToManyAnn.mappedBy();
                collectionBinder.setTargetEntity(mappings.getReflectionManager().toXClass(
                    oneToManyAnn.targetEntity()));

                collectionBinder.setCascadeStrategy(getCascadeStrategy(oneToManyAnn.cascade(),
                    hibernateCascade));
                collectionBinder.setOneToMany(true);
            }
            else if (collectionOfElementsAnn != null)
            {
                for (Ejb3JoinColumn column : joinColumns)
                {
                    if (column.isSecondary())
                    {
                        throw new NotYetImplementedException(
                            "Collections having FK in secondary table");
                    }
                }
                collectionBinder.setFkJoinColumns(joinColumns);
                mappedBy = "";
                collectionBinder.setTargetEntity(mappings.getReflectionManager().toXClass(
                    collectionOfElementsAnn.targetElement()));

                collectionBinder.setOneToMany(true);
            }
            else if (manyToManyAnn != null)
            {
                mappedBy = manyToManyAnn.mappedBy();
                collectionBinder.setTargetEntity(mappings.getReflectionManager().toXClass(
                    manyToManyAnn.targetEntity()));

                collectionBinder.setCascadeStrategy(getCascadeStrategy(manyToManyAnn.cascade(),
                    hibernateCascade));
                collectionBinder.setOneToMany(false);
            }
            else if (property.isAnnotationPresent(ManyToAny.class))
            {
                mappedBy = "";
                collectionBinder.setTargetEntity(mappings.getReflectionManager().toXClass(
                    Void.TYPE));

                collectionBinder.setCascadeStrategy(getCascadeStrategy(null, hibernateCascade));
                collectionBinder.setOneToMany(false);
            }
            collectionBinder.setMappedBy(mappedBy);
            bindJoinedTableAssociation(assocTable, mappings, entityBinder, collectionBinder,
                propertyHolder, inferredData, mappedBy);

            OnDelete onDeleteAnn = (OnDelete)property.getAnnotation(OnDelete.class);
            boolean onDeleteCascade = (onDeleteAnn != null)
                                      && (OnDeleteAction.CASCADE.equals(onDeleteAnn.action()));
            collectionBinder.setCascadeDeleteEnabled(onDeleteCascade);
            if (isIdentifierMapper)
            {
                collectionBinder.setInsertable(false);
                collectionBinder.setUpdatable(false);
            }
            if (property.isAnnotationPresent(CollectionId.class))
            {
                HashMap localGenerators = (HashMap)classGenerators.clone();
                localGenerators.putAll(buildLocalGenerators(property, mappings));
                collectionBinder.setLocalGenerators(localGenerators);
            }

            collectionBinder.bind();
        }
        else
        {
            boolean isComponent = false;
            Embeddable embeddableAnn = (Embeddable)returnedClass.getAnnotation(Embeddable.class);
            Embedded embeddedAnn = (Embedded)property.getAnnotation(Embedded.class);
            isComponent = (embeddedAnn != null) || (embeddableAnn != null);

            if (isComponent)
            {
                boolean propertyAnnotated = entityBinder.isPropertyAnnotated(property);
                String propertyAccessor = entityBinder.getPropertyAccessor(property);
                bindComponent(inferredData, propertyHolder, propertyAnnotated, propertyAccessor,
                    entityBinder, isIdentifierMapper, mappings, isComponentEmbedded);
            }
            else
            {
                boolean optional = true;
                boolean lazy = false;
                if (property.isAnnotationPresent(Basic.class))
                {
                    Basic ann = (Basic)property.getAnnotation(Basic.class);
                    optional = ann.optional();
                    lazy = ann.fetch() == FetchType.LAZY;
                }

                if ((!(optional)) && (nullability != Nullability.FORCED_NULL))
                {
                    for (Ejb3Column col : columns)
                    {
                        col.forceNotNull();
                    }
                }

                PropertyBinder propBinder = new PropertyBinder();
                propBinder.setName(inferredData.getPropertyName());
                propBinder.setReturnedClassName(inferredData.getTypeName());
                propBinder.setLazy(lazy);
                propBinder.setPropertyAccessorName(inferredData.getDefaultAccess());
                propBinder.setColumns(columns);
                propBinder.setHolder(propertyHolder);
                propBinder.setProperty(property);
                propBinder.setReturnedClass(inferredData.getPropertyClass());
                propBinder.setMappings(mappings);
                if (isIdentifierMapper)
                {
                    propBinder.setInsertable(false);
                    propBinder.setUpdatable(false);
                }
                propBinder.bind();
            }

        }

        Index index = (Index)property.getAnnotation(Index.class);
        if (index != null)
        {
            if (joinColumns != null)
            {
                for (Ejb3Column column : joinColumns)
                {
                    column.addIndex(index, inSecondPass);
                }

            }
            else if (columns != null)
            {
                for (Ejb3Column column : columns)
                {
                    column.addIndex(index, inSecondPass);
                }
            }

        }

        NaturalId naturalIdAnn = (NaturalId)property.getAnnotation(NaturalId.class);
        if (naturalIdAnn != null) if (joinColumns != null)
        {
            for (Ejb3Column column : joinColumns)
            {
                column.addUniqueKey("_UniqueKey", inSecondPass);
            }
        }
        else
            for (Ejb3Column column : columns)
                column.addUniqueKey("_UniqueKey", inSecondPass);
        
       // add by wanghuawen for comment 添加自定义注释的处理
        Ejb3Column[] columArray = joinColumns != null ? joinColumns : columns;
//        CommentBinder.bindColumnComment(property, joinColumns);
//        CommentBinder.bindTableComment(clazzToProcess, persistentClass);
        CommentBinder.bindColumnComment(property, columArray);
   
    }

    private static void bindJoinedTableAssociation(JoinTable joinTableAnn,
                                                   ExtendedMappings mappings,
                                                   EntityBinder entityBinder,
                                                   CollectionBinder collectionBinder,
                                                   PropertyHolder propertyHolder,
                                                   PropertyData inferredData, String mappedBy)
    {
        TableBinder associationTableBinder = new TableBinder();
        JoinColumn[] annInverseJoins;
        JoinColumn[] annJoins;
        if (joinTableAnn != null)
        {
            collectionBinder.setExplicitAssociationTable(true);
            if (!(BinderHelper.isDefault(joinTableAnn.schema())))
                associationTableBinder.setSchema(joinTableAnn.schema());
            if (!(BinderHelper.isDefault(joinTableAnn.catalog())))
                associationTableBinder.setCatalog(joinTableAnn.catalog());
            if (!(BinderHelper.isDefault(joinTableAnn.name())))
                associationTableBinder.setName(joinTableAnn.name());
            associationTableBinder.setUniqueConstraints(joinTableAnn.uniqueConstraints());

            JoinColumn[] joins = joinTableAnn.joinColumns();
            if (joins.length == 0)
            {
                annJoins = null;
            }
            else
            {
                annJoins = joins;
            }

            JoinColumn[] inverseJoins = joinTableAnn.inverseJoinColumns();
            if (inverseJoins.length == 0)
            {
                annInverseJoins = null;
            }
            else
                annInverseJoins = inverseJoins;
        }
        else
        {
            annJoins = null;
            annInverseJoins = null;
        }
        Ejb3JoinColumn[] joinColumns = Ejb3JoinColumn.buildJoinTableJoinColumns(annJoins,
            entityBinder.getSecondaryTables(), propertyHolder, inferredData.getPropertyName(),
            mappedBy, mappings);

        Ejb3JoinColumn[] inverseJoinColumns = Ejb3JoinColumn.buildJoinTableJoinColumns(
            annInverseJoins, entityBinder.getSecondaryTables(), propertyHolder,
            inferredData.getPropertyName(), mappedBy, mappings);

        associationTableBinder.setMappings(mappings);
        collectionBinder.setTableBinder(associationTableBinder);
        collectionBinder.setJoinColumns(joinColumns);
        collectionBinder.setInverseJoinColumns(inverseJoinColumns);
    }

    private static void bindComponent(PropertyData inferredData, PropertyHolder propertyHolder,
                                      boolean propertyAnnotated, String propertyAccessor,
                                      EntityBinder entityBinder, boolean isIdentifierMapper,
                                      ExtendedMappings mappings, boolean isComponentEmbedded)
    {
        Component comp = fillComponent(propertyHolder, inferredData, propertyAnnotated,
            propertyAccessor, true, entityBinder, isComponentEmbedded, isIdentifierMapper, false,
            mappings);

        XProperty property = inferredData.getProperty();
        setupComponentTuplizer(property, comp);

        PropertyBinder binder = new PropertyBinder();
        binder.setName(inferredData.getPropertyName());
        binder.setValue(comp);
        binder.setProperty(inferredData.getProperty());
        binder.setPropertyAccessorName(inferredData.getDefaultAccess());
        Property prop = binder.make();
        propertyHolder.addProperty(prop);
    }

    public static Component fillComponent(PropertyHolder propertyHolder,
                                          PropertyData inferredData, boolean propertyAnnotated,
                                          String propertyAccessor, boolean isNullable,
                                          EntityBinder entityBinder, boolean isComponentEmbedded,
                                          boolean isIdentifierMapper, boolean inSecondPass,
                                          ExtendedMappings mappings)
    {
        Component comp = new Component(propertyHolder.getPersistentClass());
        comp.setEmbedded(isComponentEmbedded);

        comp.setTable(propertyHolder.getTable());
        if (!(isIdentifierMapper))
        {
            comp.setComponentClassName(inferredData.getClassOrElementName());
        }
        else
        {
            comp.setComponentClassName(comp.getOwner().getClassName());
        }
        comp.setNodeName(inferredData.getPropertyName());
        String subpath = StringHelper.qualify(propertyHolder.getPath(),
            inferredData.getPropertyName());
        log.debug("Binding component with path: " + subpath);
        PropertyHolder subHolder = PropertyHolderBuilder.buildPropertyHolder(comp, subpath,
            inferredData, propertyHolder, mappings);

        List<PropertyData> classElements = new ArrayList();
        XClass returnedClassOrElement = inferredData.getClassOrElement();
        addElementsOfAClass(classElements, subHolder, propertyAnnotated, propertyAccessor,
            returnedClassOrElement, mappings);

        XClass superClass = inferredData.getPropertyClass().getSuperclass();
        while ((superClass != null) && (superClass.isAnnotationPresent(MappedSuperclass.class)))
        {
            addElementsOfAClass(classElements, subHolder,
                entityBinder.isPropertyAnnotated(superClass), propertyAccessor, superClass,
                mappings);

            superClass = superClass.getSuperclass();
        }
        for (PropertyData propertyAnnotatedElement : classElements)
        {
            processElementAnnotations(subHolder,
                (isNullable) ? Nullability.NO_CONSTRAINT : Nullability.FORCED_NOT_NULL,
                propertyAnnotatedElement.getProperty(), propertyAnnotatedElement, new HashMap(),
                entityBinder, isIdentifierMapper, isComponentEmbedded, inSecondPass, mappings);
        }

        return comp;
    }

    private static void bindId(String generatorType, String generatorName,
                               PropertyData inferredData, Ejb3Column[] columns,
                               PropertyHolder propertyHolder,
                               Map<String, IdGenerator> localGenerators, boolean isComposite,
                               boolean isPropertyAnnotated, String propertyAccessor,
                               EntityBinder entityBinder, Type typeAnn, boolean isEmbedded,
                               boolean isIdentifierMapper, ExtendedMappings mappings)
    {
        PersistentClass persistentClass = propertyHolder.getPersistentClass();
        if (!(persistentClass instanceof RootClass))
        {
            throw new AnnotationException("Unable to define/override @Id(s) on a subclass: "
                                          + propertyHolder.getEntityName());
        }

        RootClass rootClass = (RootClass)persistentClass;
        String persistentClassName = (rootClass == null) ? null : rootClass.getClassName();
        SimpleValue id;
        if (isComposite)
        {
            id = fillComponent(propertyHolder, inferredData, isPropertyAnnotated,
                propertyAccessor, false, entityBinder, isEmbedded, isIdentifierMapper, false,
                mappings);

            Component componentId = (Component)id;
            componentId.setKey(true);
            if (rootClass.getIdentifier() != null)
            {
                throw new AnnotationException(
                    componentId.getComponentClassName()
                        + " must not have @Id properties when used as an @EmbeddedId");
            }
            if (componentId.getPropertySpan() == 0)
            {
                throw new AnnotationException(componentId.getComponentClassName()
                                              + " has no persistent id property");
            }

            XProperty property = inferredData.getProperty();
            setupComponentTuplizer(property, componentId);
        }
        else
        {
            for (Ejb3Column column : columns)
            {
                column.forceNotNull();
            }
            SimpleValueBinder value = new SimpleValueBinder();
            value.setPropertyName(inferredData.getPropertyName());
            value.setReturnedClassName(inferredData.getTypeName());
            value.setColumns(columns);
            value.setPersistentClassName(persistentClassName);
            value.setMappings(mappings);
            value.setExplicitType(typeAnn);
            id = value.make();
        }
        rootClass.setIdentifier(id);
        BinderHelper.makeIdGenerator(id, generatorType, generatorName, mappings, localGenerators);
        if (isEmbedded)
        {
            rootClass.setEmbeddedIdentifier(inferredData.getPropertyClass() == null);
        }
        else
        {
            PropertyBinder binder = new PropertyBinder();
            binder.setName(inferredData.getPropertyName());
            binder.setValue(id);
            binder.setPropertyAccessorName(inferredData.getDefaultAccess());
            binder.setProperty(inferredData.getProperty());
            Property prop = binder.make();
            rootClass.setIdentifierProperty(prop);
        }
    }

    private static void setupComponentTuplizer(XProperty property, Component component)
    {
        if (property == null) return;
        if (property.isAnnotationPresent(Tuplizers.class))
        {
            for (Tuplizer tuplizer : ((Tuplizers)property.getAnnotation(Tuplizers.class)).value())
            {
                EntityMode mode = EntityMode.parse(tuplizer.entityMode());
                component.addTuplizer(mode, tuplizer.impl().getName());
            }
        }
        if (property.isAnnotationPresent(Tuplizer.class))
        {
            Tuplizer tuplizer = (Tuplizer)property.getAnnotation(Tuplizer.class);
            EntityMode mode = EntityMode.parse(tuplizer.entityMode());
            component.addTuplizer(mode, tuplizer.impl().getName());
        }
    }

    private static void bindManyToOne(String cascadeStrategy, Ejb3JoinColumn[] columns,
                                      boolean optional, boolean ignoreNotFound,
                                      boolean cascadeOnDelete, XClass targetEntity,
                                      PropertyHolder propertyHolder, PropertyData inferredData,
                                      boolean unique, boolean isIdentifierMapper,
                                      boolean inSecondPass, ExtendedMappings mappings)
    {
        org.hibernate.mapping.ManyToOne value = new org.hibernate.mapping.ManyToOne(
            columns[0].getTable());
        if (isDefault(targetEntity, mappings))
        {
            value.setReferencedEntityName(inferredData.getClassOrElementName());
        }
        else
        {
            value.setReferencedEntityName(targetEntity.getName());
        }
        defineFetchingStrategy(value, inferredData.getProperty());

        value.setIgnoreNotFound(ignoreNotFound);
        value.setCascadeDeleteEnabled(cascadeOnDelete);

        if (!(optional))
        {
            for (Ejb3JoinColumn column : columns)
            {
                column.setNullable(false);
            }
        }
        value.setTypeName(inferredData.getClassOrElementName());
        String propertyName = inferredData.getPropertyName();
        value.setTypeUsingReflection(propertyHolder.getClassName(), propertyName);

        ForeignKey fk = (ForeignKey)inferredData.getProperty().getAnnotation(ForeignKey.class);
        String fkName = (fk != null) ? fk.name() : "";

        if (!(BinderHelper.isDefault(fkName))) value.setForeignKeyName(fkName);

        String path = propertyHolder.getPath() + "." + propertyName;
        FkSecondPass secondPass = new ToOneFkSecondPass(value, columns, (!(optional)) && (unique),
            propertyHolder.getEntityOwnerClassName(), path, mappings);

        if (inSecondPass)
        {
            secondPass.doSecondPass(mappings.getClasses());
        }
        else
        {
            mappings.addSecondPass(secondPass);
        }

        Ejb3Column.checkPropertyConsistency(columns, propertyHolder.getEntityName() + propertyName);
        PropertyBinder binder = new PropertyBinder();
        binder.setName(propertyName);
        binder.setValue(value);

        if (isIdentifierMapper)
        {
            binder.setInsertable(false);
            binder.setUpdatable(false);
        }
        else
        {
            binder.setInsertable(columns[0].isInsertable());
            binder.setUpdatable(columns[0].isUpdatable());
        }
        binder.setPropertyAccessorName(inferredData.getDefaultAccess());
        binder.setCascade(cascadeStrategy);
        Property prop = binder.make();

        propertyHolder.addProperty(prop, columns);
    }

    protected static void defineFetchingStrategy(ToOne toOne, XProperty property)
    {
        LazyToOne lazy = (LazyToOne)property.getAnnotation(LazyToOne.class);
        Fetch fetch = (Fetch)property.getAnnotation(Fetch.class);
        javax.persistence.ManyToOne manyToOne = (javax.persistence.ManyToOne)property.getAnnotation(javax.persistence.ManyToOne.class);
        OneToOne oneToOne = (OneToOne)property.getAnnotation(OneToOne.class);
        FetchType fetchType;
        if (manyToOne != null)
        {
            fetchType = manyToOne.fetch();
        }
        else
        {
            if (oneToOne != null)
            {
                fetchType = oneToOne.fetch();
            }
            else
                throw new AssertionFailure(
                    "Define fetch strategy on a property not annotated with @OneToMany nor @OneToOne");
        }
        if (lazy != null)
        {
            toOne.setLazy(lazy.value() != LazyToOneOption.FALSE);
            toOne.setUnwrapProxy(lazy.value() == LazyToOneOption.NO_PROXY);
        }
        else
        {
            toOne.setLazy(fetchType == FetchType.LAZY);
            toOne.setUnwrapProxy(false);
        }
        if (fetch != null)
        {
            if (fetch.value() == org.hibernate.annotations.FetchMode.JOIN)
            {
                toOne.setFetchMode(org.hibernate.FetchMode.JOIN);
                toOne.setLazy(false);
                toOne.setUnwrapProxy(false);
                return;
            }
            if (fetch.value() == org.hibernate.annotations.FetchMode.SELECT)
            {
                toOne.setFetchMode(org.hibernate.FetchMode.SELECT);
                return;
            }
            if (fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT)
            {
                throw new AnnotationException(
                    "Use of FetchMode.SUBSELECT not allowed on ToOne associations");
            }

            throw new AssertionFailure("Unknown FetchMode: " + fetch.value());
        }

        toOne.setFetchMode(getFetchMode(fetchType));
    }

    private static void bindOneToOne(String cascadeStrategy, Ejb3JoinColumn[] joinColumns,
                                     boolean optional, org.hibernate.FetchMode fetchMode,
                                     boolean ignoreNotFound, boolean cascadeOnDelete,
                                     XClass targetEntity, PropertyHolder propertyHolder,
                                     PropertyData inferredData, String mappedBy,
                                     boolean trueOneToOne, boolean isIdentifierMapper,
                                     boolean inSecondPass, ExtendedMappings mappings)
    {
        String propertyName = inferredData.getPropertyName();
        log.debug("Fetching " + propertyName + " with " + fetchMode);
        boolean mapToPK = true;
        if (!(trueOneToOne))
        {
            KeyValue identifier = propertyHolder.getIdentifier();
            if (identifier == null)
            {
                mapToPK = false;
            }
            else
            {
                Iterator idColumns = identifier.getColumnIterator();
                List idColumnNames = new ArrayList();

                while (idColumns.hasNext())
                {
                    org.hibernate.mapping.Column currentColumn = (org.hibernate.mapping.Column)idColumns.next();
                    idColumnNames.add(currentColumn.getName());
                }
                for (Ejb3JoinColumn col : joinColumns)
                {
                    if (!(idColumnNames.contains(col.getMappingColumn().getName())))
                    {
                        mapToPK = false;
                        break;
                    }
                }
            }
        }
        if ((trueOneToOne) || (mapToPK) || (!(BinderHelper.isDefault(mappedBy))))
        {
            OneToOneSecondPass secondPass = new OneToOneSecondPass(mappedBy,
                propertyHolder.getEntityName(), propertyName, propertyHolder, inferredData,
                targetEntity, ignoreNotFound, cascadeOnDelete, optional, cascadeStrategy,
                joinColumns, mappings);

            if (inSecondPass)
            {
                secondPass.doSecondPass(mappings.getClasses());
            }
            else
            {
                mappings.addSecondPass(secondPass, BinderHelper.isDefault(mappedBy));
            }

        }
        else
        {
            bindManyToOne(cascadeStrategy, joinColumns, optional, ignoreNotFound, cascadeOnDelete,
                targetEntity, propertyHolder, inferredData, true, isIdentifierMapper,
                inSecondPass, mappings);
        }
    }

    private static void bindAny(String cascadeStrategy, Ejb3JoinColumn[] columns,
                                boolean cascadeOnDelete, Nullability nullability,
                                PropertyHolder propertyHolder, PropertyData inferredData,
                                EntityBinder entityBinder, boolean isIdentifierMapper,
                                ExtendedMappings mappings)
    {
        org.hibernate.annotations.Any anyAnn = (org.hibernate.annotations.Any)inferredData.getProperty().getAnnotation(
            org.hibernate.annotations.Any.class);
        if (anyAnn == null)
        {
            throw new AssertionFailure("Missing @Any annotation: "
                                       + StringHelper.qualify(propertyHolder.getPath(),
                                           inferredData.getPropertyName()));
        }

        org.hibernate.mapping.Any value = BinderHelper.buildAnyValue(anyAnn.metaDef(), columns,
            anyAnn.metaColumn(), inferredData, cascadeOnDelete, nullability, propertyHolder,
            entityBinder, anyAnn.optional(), mappings);

        PropertyBinder binder = new PropertyBinder();
        binder.setName(inferredData.getPropertyName());
        binder.setValue(value);

        binder.setLazy(anyAnn.fetch() == FetchType.LAZY);

        if (isIdentifierMapper)
        {
            binder.setInsertable(false);
            binder.setUpdatable(false);
        }
        else
        {
            binder.setInsertable(columns[0].isInsertable());
            binder.setUpdatable(columns[0].isUpdatable());
        }
        binder.setPropertyAccessorName(inferredData.getDefaultAccess());
        binder.setCascade(cascadeStrategy);
        Property prop = binder.make();

        propertyHolder.addProperty(prop, columns);
    }

    private static String generatorType(GenerationType generatorEnum) {
        switch (generatorEnum.ordinal())
        {
        case 1:
          return "identity";
        case 2:
          return "native";
        case 3:
          return MultipleHiLoPerTableGenerator.class.getName();
        case 4:
          return "seqhilo";
        }
        throw new AssertionFailure("Unknown GeneratorType: " + generatorEnum);
  }

    private static EnumSet<org.hibernate.annotations.CascadeType> convertToHibernateCascadeType(javax.persistence.CascadeType[] ejbCascades) {
    EnumSet hibernateCascadeSet = EnumSet.noneOf(org.hibernate.annotations.CascadeType.class);
    if ((ejbCascades != null) && (ejbCascades.length > 0)) {
      for (javax.persistence.CascadeType cascade : ejbCascades) {
        switch (cascade.ordinal())
        {
        case 1:
          hibernateCascadeSet.add(org.hibernate.annotations.CascadeType.ALL);
          break;
        case 2:
          hibernateCascadeSet.add(org.hibernate.annotations.CascadeType.PERSIST);
          break;
        case 3:
          hibernateCascadeSet.add(org.hibernate.annotations.CascadeType.MERGE);
          break;
        case 4:
          hibernateCascadeSet.add(org.hibernate.annotations.CascadeType.REMOVE);
          break;
        case 5:
          hibernateCascadeSet.add(org.hibernate.annotations.CascadeType.REFRESH);
        }
      }

    }

    return hibernateCascadeSet;
  }

    private static String getCascadeStrategy(javax.persistence.CascadeType[] ejbCascades, Cascade hibernateCascadeAnnotation)
  {
    EnumSet hibernateCascadeSet = convertToHibernateCascadeType(ejbCascades);
    org.hibernate.annotations.CascadeType[] hibernateCascades = (hibernateCascadeAnnotation == null) ? null : hibernateCascadeAnnotation.value();

    if ((hibernateCascades != null) && (hibernateCascades.length > 0)) {
      for (org.hibernate.annotations.CascadeType cascadeType : hibernateCascades) {
        hibernateCascadeSet.add(cascadeType);
      }
    }

    StringBuilder cascade = new StringBuilder();
    Iterator cascadeType = hibernateCascadeSet.iterator();
    while (cascadeType.hasNext()) {
      switch (((org.hibernate.annotations.CascadeType)cascadeType.next()).ordinal())
      {
      case 1:
        cascade.append(",").append("all");
        break;
      case 2:
        cascade.append(",").append("save-update");
        break;
      case 3:
        cascade.append(",").append("persist");
        break;
      case 4:
        cascade.append(",").append("merge");
        break;
      case 5:
        cascade.append(",").append("lock");
        break;
      case 6:
        cascade.append(",").append("refresh");
        break;
      case 7:
        cascade.append(",").append("replicate");
        break;
      case 8:
        cascade.append(",").append("evict");
        break;
      case 9:
        cascade.append(",").append("delete");
        break;
      case 10:
        cascade.append(",").append("delete-orphan");
        break;
      case 11:
        cascade.append(",").append("delete");
      }
    }

    return ((cascade.length() > 0) ? cascade.substring(1) : "none");
  }

    public static org.hibernate.FetchMode getFetchMode(FetchType fetch)
    {
        if (fetch == FetchType.EAGER)
        {
            return org.hibernate.FetchMode.JOIN;
        }

        return org.hibernate.FetchMode.SELECT;
    }

    private static HashMap<String, IdGenerator> buildLocalGenerators(XAnnotatedElement annElt,
                                                                     Mappings mappings)
    {
        HashMap generators = new HashMap();
        TableGenerator tabGen = (TableGenerator)annElt.getAnnotation(TableGenerator.class);
        SequenceGenerator seqGen = (SequenceGenerator)annElt.getAnnotation(SequenceGenerator.class);
        GenericGenerator genGen = (GenericGenerator)annElt.getAnnotation(GenericGenerator.class);
        if (tabGen != null)
        {
            IdGenerator idGen = buildIdGenerator(tabGen, mappings);
            generators.put(idGen.getName(), idGen);
        }
        if (seqGen != null)
        {
            IdGenerator idGen = buildIdGenerator(seqGen, mappings);
            generators.put(idGen.getName(), idGen);
        }
        if (genGen != null)
        {
            IdGenerator idGen = buildIdGenerator(genGen, mappings);
            generators.put(idGen.getName(), idGen);
        }
        return generators;
    }

    public static boolean isDefault(XClass clazz, ExtendedMappings mappings)
    {
        return mappings.getReflectionManager().equals(clazz, Void.TYPE);
    }

    public static Map<XClass, InheritanceState> buildInheritanceStates(List<XClass> orderedClasses,
                                                                       ReflectionManager reflectionManager)
    {
        Map inheritanceStatePerClass = new HashMap(orderedClasses.size());

        for (XClass clazz : orderedClasses)
        {
            InheritanceState superclassState = InheritanceState.getSuperclassInheritanceState(
                clazz, inheritanceStatePerClass, reflectionManager);

            InheritanceState state = new InheritanceState(clazz);
            if (superclassState != null)
            {
                superclassState.hasSons = true;
                InheritanceState superEntityState = InheritanceState.getSuperEntityInheritanceState(
                    clazz, inheritanceStatePerClass, reflectionManager);

                state.hasParents = (superEntityState != null);
                boolean nonDefault = (state.type != null)
                                     && (!(InheritanceType.SINGLE_TABLE.equals(state.type)));
                if (superclassState.type != null)
                {
                    boolean mixingStrategy = (state.type != null)
                                             && (!(state.type.equals(superclassState.type)));
                    if ((nonDefault) && (mixingStrategy))
                    {
                        log.warn("Mixing inheritance strategy in a entity hierarchy is not allowed, ignoring sub strategy in: "
                                 + clazz.getName());
                    }

                    state.type = superclassState.type;
                }
            }
            inheritanceStatePerClass.put(clazz, state);
        }
        return inheritanceStatePerClass;
    }

    static
    {
        log = LogFactory.getLog(AnnotationBinder.class);
    }
}